import sys
import re
import random

sys.path.append('./')
sys.path.append('/Users/xuchaosheng/anaconda3/lib/python3.6/site-packages')

from config import *

client = MongoClient('127.0.0.1', 27017, connect = False)

knowledge_graph_db = client['knowledge_graph_db']
knowledge_graph_coll = knowledge_graph_db['knowledge_graph_coll']

nid2name = {item['nid']: item['name'] for item in knowledge_graph_coll.find()}


def node_id():
    return ''.join(random.choices('0123456789abcdefghijklmnopqrstuvwxyz', k = 20))


def search_node(nid):
    node = knowledge_graph_coll.find_one({'nid': nid})

    if not node:
        return

    children = set(node['children'])

    # 将同义节点的子节点也加入到查询计划中
    for alias_nid in node['alias']:
        alias_node = knowledge_graph_coll.find_one({'nid': alias_nid})
        children.update(set(alias_node['children']))

    data = {
        'key': node['name'],
        'alias': [nid2name[nid] for nid in node['alias']],
        'value': [search_node(child_nid) for child_nid in children]
    }

    return data


def insert_node(name):
    """
    新建一个节点
    :param name:
    :return:
    """
    if knowledge_graph_coll.find_one({'name': name}):
        return

    nid = node_id()

    knowledge_graph_coll.insert_one({'name': name, 'children': [], 'parent': [], 'alias': [], 'nid': nid})
    nid2name[nid] = name

    return nid


def delete_node(name):
    """
    删除一个节点
    :param name:
    :return:
    """
    if not knowledge_graph_coll.find_one({'name': name}):
        return

    node = knowledge_graph_coll.find_one({'name': name})

    # 遍历父节点解除与本节点的父子关系
    for par_nid in node['parent']:
        knowledge_graph_coll.update_one({'nid': par_nid}, {'$pull': {
            'children': node['nid']
        }})

    # 遍历同义节点解除与本节点的同义关系
    for alias_nid in node['alias']:
        knowledge_graph_coll.update_one({'nid': alias_nid}, {'$pull': {
            'alias': node['nid']
        }})

    # 遍历子节点解除与本节点的父子关系
    for child_nid in node['children']:
        knowledge_graph_coll.update_one({'nid': child_nid}, {'$pull': {
            'parent': node['nid']
        }})

    knowledge_graph_coll.delete_one({'name': name})


def update_node(oldname, newname):
    """
    更新节点名称
    :param oldname:
    :param newname:
    :return:
    """
    if not knowledge_graph_coll.find_one({'name': oldname}):
        return 1, oldname + '节点暂不存在'

    knowledge_graph_coll.update_one({'name': oldname}, {'$set': {
        'name': newname
    }})

    for nid in nid2name:
        if nid2name[nid] == oldname:
            nid2name[nid] = newname


def insert_child(parent_name, child_name):
    """
    新增父子节点关系
    :param parent_name:
    :param child_name:
    :return:
    """
    if parent_name == child_name:
        return

    if not knowledge_graph_coll.find_one({'name': parent_name}):
        return 1, parent_name + '节点暂不存在'

    parnet_node = knowledge_graph_coll.find_one({'name': parent_name})

    # 如果子节点本身还不在数据库中
    if not knowledge_graph_coll.find_one({'name': child_name}):
        child_nid = insert_node(child_name)
    else:
        child_node = knowledge_graph_coll.find_one({'name': child_name})
        child_nid = child_node['nid']

    if child_nid in parnet_node['alias']:
        return

    if child_nid == parnet_node['nid']:
        return

    # 将子节点id插入到父节点children字段中
    knowledge_graph_coll.update_one({'name': parent_name}, {'$addToSet': {
        'children': child_nid
    }})

    # 将父节点id插入到子节点parent字段中
    knowledge_graph_coll.update_one({'name': child_name}, {'$addToSet': {
        'parent': parnet_node['nid']
    }})


def delete_child(parent_name, child_name):
    """
    解除父子节点关系
    :param parent_name:
    :param child_name:
    :return:
    """
    if not knowledge_graph_coll.find_one({'name': parent_name}):
        return 1, parent_name + '节点暂不存在'

    if not knowledge_graph_coll.find_one({'name': child_name}):
        return 1, child_name + '节点暂不存在'

    parent_node = knowledge_graph_coll.find_one({'name': parent_name})
    child_node = knowledge_graph_coll.find_one({'name': child_name})

    knowledge_graph_coll.update_one({'name': parent_name}, {'$pull': {
        'children': child_node['nid']
    }})

    # 同时在parent_name的同义节点中也解除与child_name的父子节点关系
    for alias_nid in parent_node['alias']:
        knowledge_graph_coll.update_one({'nid': alias_nid}, {'$pull': {
            'children': child_node['nid']
        }})

    knowledge_graph_coll.update_one({'name': child_name}, {'$pull': {
        'parent': parent_node['nid']
    }})


def insert_alias(name, alias_name):
    """
    新增同义节点关系
    :param name:
    :param alias_name:
    :return:
    """
    if not knowledge_graph_coll.find_one({'name': name}):
        return 1, name + '节点暂不存在'

    node = knowledge_graph_coll.find_one({'name': name})

    # 如果alias_name节点还不在数据库中，则临时新建一个节点
    if not knowledge_graph_coll.find_one({'name': alias_name}):
        alias_nid = insert_node(alias_name)
    else:
        alias_node = knowledge_graph_coll.find_one({'name': alias_name})
        alias_nid = alias_node['nid']

    if alias_nid in node['alias']:
        return

    knowledge_graph_coll.update_one({'name': name}, {'$addToSet': {
        'alias': alias_nid
    }})

    knowledge_graph_coll.update_one({'name': alias_name}, {'$addToSet': {
        'alias': node['nid']
    }})

    # 遍历主节点的所有同义节点，与该新节点相互新增一次同义关系
    for i in node['alias']:
        knowledge_graph_coll.update_one({'name': alias_name}, {'$addToSet': {
            'alias': i
        }})

        knowledge_graph_coll.update_one({'nid': i}, {'$addToSet': {
            'alias': alias_nid
        }})


def delete_alias(name, alias_name):
    """
    解除同义节点关系
    :param name:
    :param alias_name:
    :return:
    """
    if not knowledge_graph_coll.find_one({'name': name}):
        return 1, name + '节点暂不存在'

    if not knowledge_graph_coll.find_one({'name': alias_name}):
        return 1, alias_name + '节点暂不存在'

    node = knowledge_graph_coll.find_one({'name': name})
    alias_node = knowledge_graph_coll.find_one({'name': alias_name})

    knowledge_graph_coll.update_one({'name': name}, {'$pull': {'alias': alias_node['nid']}})
    knowledge_graph_coll.update_one({'name': alias_name}, {'$pull': {'alias': node['nid']}})

    # 遍历主节点的所有同义节点，与该节点相互解除同义关系
    for i in node['alias']:
        knowledge_graph_coll.update_one({'name': alias_name}, {'$pull': {'alias': i}})
        knowledge_graph_coll.update_one({'nid': i}, {'$pull': {'alias': alias_node['nid']}})


def search_by_name(name = ''):
    if name:
        return [item['name'] for item in knowledge_graph_coll.find({'name': {'$regex': name, '$options': 'i'}})]
    else:
        return [item['name'] for item in knowledge_graph_coll.find()]


@app.route('/trainPlatform/knowledge_items.do', methods = ['GET'])
def knowledge_items():
    kw = request.args.get('kw', '').strip().lower()
    data = search_by_name(kw)

    resp = {
        'success': True,
        'data': data
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/knowledge_search.do', methods = ['GET'])
def knowledge_search():
    key = request.args.get('key', '').strip().lower()

    if not knowledge_graph_coll.find_one({'name': key}):
        resp = {
            'success': False,
            'msg': '此节点暂不存在'
        }

        response = make_response(jsonify(resp))
        return response

    node = knowledge_graph_coll.find_one({'name': key})
    data = search_node(node['nid'])

    resp = {
        'success': True,
        'data': data
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/insert_node.do', methods = ['GET'])
def insert_node_():
    root = request.args.get('root', '').strip().lower()
    leaf = request.args.get('leaf', '').strip().lower()

    if not root or not leaf:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    insert_child(root, leaf)

    resp = {
        'success': True,
        'msg': '成功插入'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/delete_node.do', methods = ['GET'])
def delete_node_():
    root = request.args.get('root', '').strip().lower()
    leaf = request.args.get('leaf', '').strip().lower()

    if not root or not leaf:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    delete_child(root, leaf)

    resp = {
        'success': True,
        'msg': '解除成功'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/addSynonym.do', methods = ['GET'])
def addSynonym():
    root = request.args.get('root', '').strip().lower()
    leaf = request.args.get('leaf', '').strip().lower()

    insert_alias(root, leaf)

    resp = {
        'success': True,
        'msg': '添加成功'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/delSynonym.do', methods = ['GET'])
def delSynonym():
    root = request.args.get('root', '').strip().lower()
    leaf = request.args.get('leaf', '').strip().lower()

    if not root or not leaf:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    delete_alias(root, leaf)

    resp = {
        'success': True,
        'msg': '删除成功'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/newNode.do', methods = ['GET'])
def newNode():
    name = request.args.get('name', '').strip().lower()

    if not name:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    insert_node(name)

    resp = {
        'success': True,
        'msg': '新建一个' + name + '节点'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/delNode.do', methods = ['GET'])
def delNode():
    name = request.args.get('name', '').strip().lower()

    if not name:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    delete_node(name)

    resp = {
        'success': True,
        'msg': '已删除' + name + '节点'
    }

    response = make_response(jsonify(resp))
    return response


@app.route('/trainPlatform/update_node_name.do', methods = ['GET'])
def update_node_name():
    oldname = request.args.get('oldname', '').strip().lower()
    newname = request.args.get('newname', '').strip().lower()

    if not oldname or not newname:
        resp = {
            'success': False,
            'msg': '节点名称不能为空'
        }

        response = make_response(jsonify(resp))
        return response

    update_node(oldname, newname)

    resp = {
        'success': False,
        'msg': '成功编辑'
    }

    response = make_response(jsonify(resp))
    return response
